`timescale 1ns/1ps
`default_nettype none

/* NOTE:
* - 产生智能设置时用到的图像pattern
*/

module pattern_gen (
    // system signal
    input  wire         I_sclk,  // 125M
    input  wire         I_rst_n,
    // config
    input  wire [3:0]   I_cfg_pattern_mode,   // pattern工作模式
    input  wire [2:0]   I_cfg_pattern_rgb,    // pattern颜色
    input  wire [7:0]   I_cfg_pattern_col,    // pattern当前列
    input  wire [7:0]   I_cfg_pattern_row,    // pattern当前行
    input  wire [7:0]   I_cfg_pattern_pos_x,  // pattern走行时x坐标
    input  wire [10:0]  I_cfg_pattern_width,  // pattern宽度
    input  wire [10:0]  I_cfg_pattern_height, // pattern高度
    input  wire [10:0]  I_cfg_pattern_hblank, // pattern行间隔时间
    input  wire [10:0]  I_cfg_pattern_vblank, // pattern列间隔
    // status
    output wire         O_status_pattern_en,
    // input  frame
    input  wire         I_frame_start,
    input  wire         I_frame_end,
    input  wire         I_row_end,
    input  wire         I_burst_start,
    input  wire [12:0]  I_burst_row,
    input  wire [12:0]  I_burst_col,
    input  wire         I_pixel_en,
    input  wire [7:0]   I_pixel_data,
    // output frame
    output reg          O_frame_start,
    output reg          O_frame_end,
    output reg          O_row_end,
    output reg          O_burst_start,
    output reg  [12:0]  O_burst_row,
    output reg  [12:0]  O_burst_col,
    output reg          O_pixel_en,
    output reg  [7:0]   O_pixel_data
);
//------------------------Parameter----------------------
// mode
localparam [2:0]
    NONE  = 0,
    SOLID = 1,
    LINE  = 2,
    COL   = 3,
    ROW   = 4,
    DONE  = 5,
    BOX0  = 6,
    BOX1  = 7;

// fsm
localparam [3:0]
    IDLE  = 0,
    START = 1,
    WAIT0 = 2,
    DAT_R = 3,
    DAT_G = 4,
    DAT_B = 5,
    LOOP  = 6,
    OVER  = 7,
    WAIT1 = 8;

//------------------------Local signal-------------------
// fsm
reg  [3:0]  state;
reg  [3:0]  next;
reg         enable;
reg  [9:0]  x;
reg  [9:0]  y;
reg  [11:0] cnt;

// output frame
reg  [15:0] timer_2kHz;
reg         tick_2kHz;
reg  [9:0]  timer_2Hz;
reg         blink;

//------------------------Instantiation------------------

//------------------------Body---------------------------
//{{{+++++++++++++++++++++fsm++++++++++++++++++++++++++++
// state
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        state <= IDLE;
    else if (~enable)
        state <= IDLE;
    else
        state <= next;
end

// next
always @(*) begin
    case (state)
        IDLE: begin
            next = START;
        end

        START: begin
            next = WAIT0;
        end

        WAIT0: begin
            if (cnt == I_cfg_pattern_hblank)
                next = DAT_R;
            else
                next = WAIT0;
        end

        DAT_R: begin
            next = DAT_G;
        end

        DAT_G: begin
            next = DAT_B;
        end

        DAT_B: begin
            if (x == I_cfg_pattern_width - 1'b1)
                next = LOOP;
            else
                next = DAT_R;
        end

        LOOP: begin
            if (y == I_cfg_pattern_height - 1'b1)
                next = OVER;
            else
                next = WAIT0;
        end

        OVER: begin
            next = WAIT1;
        end

        WAIT1: begin
            if (cnt == I_cfg_pattern_vblank)
                next = START;
            else
                next = WAIT1;
        end

        default: begin
            next = IDLE;
        end
    endcase
end

// enable
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        enable <= 1'b0;
    else if (I_cfg_pattern_mode == SOLID)
        enable <= 1'b1;
    else if (I_cfg_pattern_mode == LINE)
        enable <= 1'b1;
    else if (I_cfg_pattern_mode == COL)
        enable <= 1'b1;
    else if (I_cfg_pattern_mode == ROW)
        enable <= 1'b1;
    else if (I_cfg_pattern_mode == DONE)
        enable <= 1'b1;
    else if (I_cfg_pattern_mode == BOX0)
        enable <= 1'b1;
    else if (I_cfg_pattern_mode == BOX1)
        enable <= 1'b1;
    else
        enable <= 1'b0;
end

// x
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        x <= 1'b0;
    else if (state == WAIT0)
        x <= 1'b0;
    else if (state == DAT_B)
        x <= x + 1'b1;
end

// y
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        y <= 1'b0;
    else if (state == START)
        y <= 1'b0;
    else if (state == LOOP)
        y <= y + 1'b1;
end

// cnt
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        cnt <= 1'b1;
    else if (state == START || state == LOOP)
        cnt <= 1'b1;
    else
        cnt <= cnt + 1'b1;
end
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

//{{{+++++++++++++++++++++output frame+++++++++++++++++++
// O_frame_start
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        O_frame_start <= 1'b0;
    else if (~enable)
        O_frame_start <= I_frame_start;
    else if (state == START)
        O_frame_start <= 1'b1;
    else
        O_frame_start <= 1'b0;
end

// O_frame_end
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        O_frame_end <= 1'b0;
    else if (~enable)
        O_frame_end <= I_frame_end;
    else
        O_frame_end <= (state == OVER);
end

// O_row_end
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        O_row_end <= 1'b0;
    else if (~enable)
        O_row_end <= I_row_end;
    else if (state == LOOP)
        O_row_end <= 1'b1;
    else
        O_row_end <= 1'b0;
end

// O_burst_start
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        O_burst_start <= 1'b0;
    else if (~enable)
        O_burst_start <= I_burst_start;
    else if (state == WAIT0 && next == DAT_R)
        O_burst_start <= 1'b1;
    else
        O_burst_start <= 1'b0;
end

// O_pixel_en
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        O_pixel_en <= 1'b0;
    else if (~enable)
        O_pixel_en <= I_pixel_en;
    else if (state == DAT_R || state == DAT_G || state == DAT_B)
        O_pixel_en <= 1'b1;
    else
        O_pixel_en <= 1'b0;
end

// O_pixel_data
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        O_pixel_data <= 1'b0;
    else if (~enable)
        O_pixel_data <= I_pixel_data;
    else begin
        if (I_cfg_pattern_mode == SOLID) begin
            if (state == DAT_R && I_cfg_pattern_rgb[0])
                O_pixel_data <= 8'hff;
            else if (state == DAT_G && I_cfg_pattern_rgb[1])
                O_pixel_data <= 8'hff;
            else if (state == DAT_B && I_cfg_pattern_rgb[2])
                O_pixel_data <= 8'hff;
            else
                O_pixel_data <= 8'h00;
        end
        else if (I_cfg_pattern_mode == LINE) begin
            if (y == 1'b0)
                O_pixel_data <= 8'hff;
            else
                O_pixel_data <= 8'h00;
        end
        else if (I_cfg_pattern_mode == COL) begin
            if (y != 1'b0)
                O_pixel_data <= 8'h00;
            else if (x < I_cfg_pattern_col)
                O_pixel_data <= 8'hff;
            else if (x == I_cfg_pattern_col && blink)
                O_pixel_data <= 8'hff;
            else
                O_pixel_data <= 8'h00;
        end
        else if (I_cfg_pattern_mode == ROW || I_cfg_pattern_mode == DONE) begin
            if (y == 1'b0) begin
                if (x <= I_cfg_pattern_col)
                    O_pixel_data <= 8'hff;
                else
                    O_pixel_data <= 8'h00;
            end
            else if (x != I_cfg_pattern_pos_x)
                O_pixel_data <= 8'h00;
            else if (y < I_cfg_pattern_row)
                O_pixel_data <= 8'hff;
            else if (y == I_cfg_pattern_row && (blink || I_cfg_pattern_mode == DONE))
                O_pixel_data <= 8'hff;
            else
                O_pixel_data <= 8'h00;
        end
        else if (I_cfg_pattern_mode == BOX0 || I_cfg_pattern_mode == BOX1) begin
            if (x == 1'b0 || x == I_cfg_pattern_width - 1'b1)
                O_pixel_data <= 8'hff;
            else if (y == 1'b0 || y == I_cfg_pattern_height - 1'b1)
                O_pixel_data <= 8'hff;
            else if (I_cfg_pattern_mode == BOX1 && blink)
                O_pixel_data <= 8'hff;
            else
                O_pixel_data <= 8'h00;
        end
    end
end

// O_burst_row
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        O_burst_row <= 1'b0;
    else if (~enable)
        O_burst_row <= I_burst_row;
    else
        O_burst_row <= y;
end

// O_burst_col
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        O_burst_col <= 1'b0;
    else if (~enable)
        O_burst_col <= I_burst_col;
    else
        O_burst_col <= 1'b0;
end

// timer_2kHz
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        timer_2kHz <= 1'b0;
    else if (~enable)
        timer_2kHz <= 1'b0;
    else if (tick_2kHz)
        timer_2kHz <= 1'b0;
    else
        timer_2kHz <= timer_2kHz + 1'b1;
end

// tick_2kHz
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        tick_2kHz <= 1'b0;
    else if (timer_2kHz == 16'd62498)
        tick_2kHz <= 1'b1;
    else
        tick_2kHz <= 1'b0;
end

// timer_2Hz
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        timer_2Hz <= 1'b0;
    else if (~enable)
        timer_2Hz <= 1'b0;
    else if (tick_2kHz) begin
        if (timer_2Hz == 10'd999)
            timer_2Hz <= 1'b0;
        else
            timer_2Hz <= timer_2Hz + 1'b1;
    end
end

// blink
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        blink <= 1'b1;
    else if (~enable)
        blink <= 1'b1;
    else if (tick_2kHz && timer_2Hz == 10'd999)
        blink <= ~blink;
end
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

//{{{+++++++++++++++++++++status+++++++++++++++++++++++++
assign O_status_pattern_en = enable;
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

endmodule

`default_nettype wire

// vim:set ts=4 sw=4 et fenc=utf-8 fdm=marker:
